package arcane.network;

import ggn.client.Client;
import ggn.client.DefaultClient;
import ggn.server.Server;
import ggn.server.ServerIOHandler;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.HashSet;
import java.util.List;
import java.util.Vector;

import ps.util.RandomUtil;
import arcane.constant.Constants;

public class ArcaneIOHandler extends ServerIOHandler {
	// All rooms where games are about to start.
	Vector<Room> rooms;

	/*
	 * p
	 */
	private final Room EMPTY_ROOM = new Room(null, 100);

	public ArcaneIOHandler(Server server) {
		super(server);
		rooms = new Vector<Room>();
	}

	public Socket addClient(Client client) throws IOException {
		Socket socket = super.addClient(client);
		EMPTY_ROOM.addClient(client);
		return socket;
	}

	public void removeClient(Client c) {
		super.removeClient(c);
		purgeClient(c);
	}

	private void purgeClient(Client c) {
		if (!EMPTY_ROOM.removeClient(c)) {
			try {
				forwardToRoom(EMPTY_ROOM, Constants.PARTY_REF + ".remove(\""
						+ c.getDataID() + "\");", c);
			} catch (IOException e) {
				System.err.println("IOException removing client " + c
						+ " from lounge.");
				e.printStackTrace();
			}
		} else {
			for (int i = 0; i < rooms.size(); i++) {
				if (rooms.remove(c)) {
					try {
						forwardToRoom(rooms.get(i), Constants.PARTY_REF
								+ ".remove(\"" + c.getDataID() + "\");", c);
					} catch (IOException e) {
						System.err.println("IOException removing client " + c
								+ " from party " + rooms.get(i));
						e.printStackTrace();
					}
					break;
				}
			}
		}
	}

	/**
	 * Some new client made connection to PVP server.
	 */
	public synchronized void madeNewConnection(Client client,
			ObjectInputStream ois, ObjectOutputStream oos) {
		sendRooms(ois, oos);
	}

	/**
	 * Attempts to write information to the client about up to 10 rooms current
	 * running on the server.
	 * 
	 * @param ois
	 *            client's input stream
	 * @param oos
	 *            client's output stream
	 */
	protected synchronized void sendRooms(ObjectInputStream ois,
			ObjectOutputStream oos) {
		int numRooms = rooms.size();
		if (numRooms > 10)
			numRooms = 10;
		try {
			oos.writeObject("ROOMS.clear()");
			HashSet<Integer> index = new HashSet<Integer>(numRooms);
			for (int i = 0; i < numRooms; i++) {
				int num;
				while (index.contains(num = RandomUtil.getRandom(numRooms - 1)))
					;
				index.add(num);
				Room room = rooms.get(num);
				String cmd = "new RoomDetail(\"" + room.getHost().toString()
						+ "\", \"" + room.getName() + "\", "
						+ room.getNumberClients() + ", " + room.getMaxClients()
						+ ")";
				oos.writeObject("ROOMS.add(" + cmd);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void forwardToRoom(Room r, Object obj, Client sender)
			throws IOException {
		List<Client> clients = r.getClients();
		for (int i = 0; i < clients.size(); i++) {
			clients.get(i).getOutput().writeObject(obj);
			clients.get(i).getOutput().flush();
		}
	}

	/**
	 * Objects sent to server from clients are read here. Usually, they're
	 * forwarded to other clients within the same room as this client.
	 * 
	 * NOTE: DO NOT USE client.writeObject(...) to communicate with client from
	 * server. Causes thread death for some reason... idk why yet.
	 */
	public synchronized void readFromClient(Object inputObj, Client sender) {
		try {
			if (inputObj instanceof String) {
				String s = inputObj.toString();
				if (s.startsWith(Constants.MAIN_CHAT_REF)) {
					List<Client> c = EMPTY_ROOM.getClients();
					for (int i = 0; i < c.size(); i++) {
						System.out.println("\tForward to client " + c.get(i));
						c.get(i).getOutput().writeObject(inputObj);
						c.get(i).getOutput().flush();
						System.out.println("Done forwarding to client: "
								+ clients.get(i));
					}
				}
				// Specific client has forwarded its name to other clients.
				// Now other clients need to forward their names to specific
				// client.
				else if (s.startsWith("NAME ")) {
					String name = s.substring(5);
					String cmd = Constants.PARTY_REF + ".add(\"" + name
							+ "\");";
					((DefaultClient) sender).setDataID(name);
					this.forwardToRoom(EMPTY_ROOM, cmd, sender);

					System.out.println("Fowarded " + cmd
							+ " to all clients in room.");
					List<Client> clients = EMPTY_ROOM.getClients();
					cmd = Constants.PARTY_REF + ".add(\"";
					for (int i = 0; i < clients.size(); i++) {
						Client c = clients.get(i);
						if (c != sender) {
							System.out.println("Forwarding " + c.getDataID()
									+ " to " + sender);
							sender.getOutput().writeObject(
									cmd + c.getDataID() + "\");");
							sender.getOutput().flush();
						}
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}